/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "chre/platform/linux/platform_log.h"

#include <cstdarg>
#include <cstdio>
#include <iostream>

#include "chre/platform/fatal_error.h"

namespace chre {

void PlatformLog::logLooper() {
  while (1) {
    char *logMessage = nullptr;

    {
      std::unique_lock<std::mutex> lock(mMutex);
      mConditionVariable.wait(
          lock, [this] { return (!mLogQueue.empty() || mStopLogger); });

      if (!mLogQueue.empty()) {
        // Move the log message to avoid holding a lock for longer than
        // required.
        logMessage = mLogQueue.front();
        mLogQueue.pop();
      } else if (mStopLogger) {
        // The stop logger is checked in an else-if to allow the main log queue
        // to drain when the logger is stopping.
        break;
      }
    }

    // If we get here, there must be a log message to output. This is outside of
    // the context of the lock which means that the logging thread will only be
    // blocked for the minimum amount of time.
    std::cerr << logMessage << std::endl;
    free(logMessage);
  }
}

PlatformLog::PlatformLog() {
  mLoggerThread = std::thread(&PlatformLog::logLooper, this);
}

PlatformLog::~PlatformLog() {
  {
    std::unique_lock<std::mutex> lock(mMutex);
    mStopLogger = true;
    mConditionVariable.notify_one();
  }

  mLoggerThread.join();
}

void PlatformLog::logVa(chreLogLevel /*logLevel*/, const char *formatStr,
                        va_list args) {
  char *formattedStr;
  int result = vasprintf(&formattedStr, formatStr, args);

  if (result >= 0) {
    std::unique_lock<std::mutex> lock(mMutex);
    mLogQueue.push(formattedStr);
    mConditionVariable.notify_one();
  } else {
    FATAL_ERROR("Failed to allocate log message");
  }
}

}  // namespace chre
